package data

import (
	"math"
	"strconv"
	"strings"
	"time"

	"gitcode.com/G-YT/baoyou/server/global"
	dataResp "gitcode.com/G-YT/baoyou/server/model/data/response"
	mgrModel "gitcode.com/G-YT/baoyou/server/model/manager"
	"gitcode.com/G-YT/baoyou/server/model/user"
	userResp "gitcode.com/G-YT/baoyou/server/model/user/response"
	"gitcode.com/G-YT/baoyou/server/utils"
)

type DataService struct{}

func (s *DataService) GetDataDistribution(hmID uint) (dataResp.DataDistributionResp, error) {
	var total int64
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ?", hmID).Count(&total).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}

	toUint := func(v int64) uint { return uint(v) }
	pct := func(v, t int64) int {
		if t == 0 {
			return 0
		}
		return int(math.Round(float64(v) * 100 / float64(t)))
	}

	// Types: 1 用户, 2 会员, 3 过期会员
	var t1, t2, t3 int64
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND types = 1", hmID).Count(&t1).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND types = 2", hmID).Count(&t2).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND types = 3", hmID).Count(&t3).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}

	// Sex: 1 男, 2 女
	var s1, s2 int64
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND sex = 1", hmID).Count(&s1).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}
	if err := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND sex = 2", hmID).Count(&s2).Error; err != nil {
		return dataResp.DataDistributionResp{}, err
	}

	// Ages using TIMESTAMPDIFF on birthday
	ageCount := func(min, max *int) (int64, error) {
		db := global.GVA_DB.Model(&user.User{}).Where("health_manager_id = ? AND birthday IS NOT NULL", hmID)
		if min != nil && max != nil {
			db = db.Where("TIMESTAMPDIFF(YEAR, birthday, CURDATE()) BETWEEN ? AND ?", *min, *max)
		} else if min != nil && max == nil {
			db = db.Where("TIMESTAMPDIFF(YEAR, birthday, CURDATE()) >= ?", *min)
		} else if min == nil && max != nil {
			db = db.Where("TIMESTAMPDIFF(YEAR, birthday, CURDATE()) <= ?", *max)
		}
		var c int64
		if err := db.Count(&c).Error; err != nil {
			return 0, err
		}
		return c, nil
	}

	var a0_25, a26_25, a36_45, a46_55, a56_65, a66_75, a76_85, a85p int64
	if c, err := ageCount(ptr(0), ptr(25)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a0_25 = c
	}
	if c, err := ageCount(ptr(26), ptr(35)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a26_25 = c
	}
	if c, err := ageCount(ptr(36), ptr(45)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a36_45 = c
	}
	if c, err := ageCount(ptr(46), ptr(55)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a46_55 = c
	}
	if c, err := ageCount(ptr(56), ptr(65)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a56_65 = c
	}
	if c, err := ageCount(ptr(66), ptr(75)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a66_75 = c
	}
	if c, err := ageCount(ptr(76), ptr(85)); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a76_85 = c
	}
	if c, err := ageCount(ptr(86), nil); err != nil {
		return dataResp.DataDistributionResp{}, err
	} else {
		a85p = c
	}

	resp := dataResp.DataDistributionResp{
		Total: uint(total),
		Types: []dataResp.DistributionItem{
			{Name: "用户", Value: toUint(t1), Percent: pct(t1, total)},
			{Name: "会员", Value: toUint(t2), Percent: pct(t2, total)},
			{Name: "过期会员", Value: toUint(t3), Percent: pct(t3, total)},
		},
		Sex: []dataResp.DistributionItem{
			{Name: "男", Value: toUint(s1), Percent: pct(s1, total)},
			{Name: "女", Value: toUint(s2), Percent: pct(s2, total)},
		},
		Ages: []dataResp.DistributionItem{
			{Name: "0-25", Value: toUint(a0_25), Percent: pct(a0_25, total)},
			{Name: "26-35", Value: toUint(a26_25), Percent: pct(a26_25, total)},
			{Name: "36-45", Value: toUint(a36_45), Percent: pct(a36_45, total)},
			{Name: "46-55", Value: toUint(a46_55), Percent: pct(a46_55, total)},
			{Name: "56-65", Value: toUint(a56_65), Percent: pct(a56_65, total)},
			{Name: "66-75", Value: toUint(a66_75), Percent: pct(a66_75, total)},
			{Name: "76-85", Value: toUint(a76_85), Percent: pct(a76_85, total)},
			{Name: "86+", Value: toUint(a85p), Percent: pct(a85p, total)},
		},
	}
	return resp, nil
}

func (s *DataService) GetDailyNewUserData(hmID uint) ([]userResp.UserData, error) {
	now := time.Now()
	// Normalize to local midnight
	today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
	items := make([]userResp.UserData, 0, 7)
	for i := 6; i >= 0; i-- {
		start := today.AddDate(0, 0, -i)
		end := start.Add(24 * time.Hour)
		var c int64
		if err := global.GVA_DB.Model(&user.User{}).
			Where("health_manager_id = ? AND created_at >= ? AND created_at < ?", hmID, start, end).
			Count(&c).Error; err != nil {
			return nil, err
		}
		items = append(items, userResp.UserData{Name: start.Format("01-02"), Value: uint(c)})
	}
	return items, nil
}

func ptr[T ~int](v T) *int { vv := int(v); return &vv }

// GetServiceData 获取服务数据【健管师端】
func (s *DataService) GetServiceData(hmID uint) (dataResp.ServiceDataResp, error) {
	// 会员（types=2）ID集合
	var membershipIDs []uint
	if err := global.GVA_DB.Model(&user.User{}).
		Where("health_manager_id = ? AND types = 2", hmID).
		Pluck("id", &membershipIDs).Error; err != nil {
		return dataResp.ServiceDataResp{}, err
	}

	// 已服务（当天存在消息提醒记录的用户）ID集合
	now := time.Now().In(utils.Shanghai())
	start := utils.DateStartCST(now)
	end := utils.DateEndCST(now)
	var servedIDs []uint
	if err := global.GVA_DB.Model(&mgrModel.MessageNotice{}).
		Where("hm_id = ? AND day BETWEEN ? AND ?", hmID, start, end).
		Pluck("DISTINCT user_id", &servedIDs).Error; err != nil {
		return dataResp.ServiceDataResp{}, err
	}

	// 差集：待服务 = 会员集合 - 已服务集合
	servedSet := make(map[uint]struct{}, len(servedIDs))
	for _, id := range servedIDs {
		servedSet[id] = struct{}{}
	}
	var pendingIDs []uint
	for _, id := range membershipIDs {
		if _, ok := servedSet[id]; !ok {
			pendingIDs = append(pendingIDs, id)
		}
	}

	toCSV := func(ids []uint) string {
		if len(ids) == 0 {
			return ""
		}
		ss := make([]string, 0, len(ids))
		for _, id := range ids {
			ss = append(ss, fmtUint(id))
		}
		return strings.Join(ss, ",")
	}

	resp := dataResp.ServiceDataResp{
		MembershipCount: uint(len(membershipIDs)),
		MembershipIDs:   toCSV(membershipIDs),
		ServedCount:     uint(len(servedIDs)),
		ServedIDs:       toCSV(servedIDs),
		PendingCount:    uint(len(pendingIDs)),
		PendingIDs:      toCSV(pendingIDs),
	}
	return resp, nil
}

func fmtUint(v uint) string { return strconv.FormatUint(uint64(v), 10) }
